package watch

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"time"
)

//Listener 当文件发生改变时调用的接口
type Listener interface {
	Listen(Configuration, Configuration)
}

//errorNews为自定义错误消息
type errorNews string

//Configuration 储存读取ini文件得到的信息
type Configuration struct {
	name       string
	section    map[string]*Section
	sectionNum int
}

var (
	//LineBreak 换行符号
	LineBreak byte = '\n'

	//CommentSign ini配置文件中注释符号
	//Linus: #, Windows: ;
	CommentSign byte = '#'
)

func init() {
	if runtime.GOOS == "windows" {
		CommentSign = ';'
	}
}

func (err errorNews) Error() string {
	return fmt.Sprintf(string(err))
}

//GetSectionNum 返回Configuration类型中的section字段数目
func (Conf Configuration) GetSectionNum() int {
	return Conf.sectionNum
}

//GetSection 返回Configuration类型中的section
func (Conf Configuration) GetSection() map[string]*Section {
	NewSection := make(map[string]*Section)
	for sectionKey, section := range Conf.section {
		NewSection[sectionKey] = &Section{make(map[string]string), 0}
		for key, value := range section.key {
			NewSection[sectionKey].key[key] = value
		}
		NewSection[sectionKey].keyNum = section.keyNum
	}
	return NewSection
}

//GetKey 返回字段为section的key
func (Conf Configuration) GetKey(section string) *Section {
	return Conf.section[section]
}

//ExistSection 判断Section是否已经存在于Configuration.section中
func (Conf Configuration) ExistSection(section string) bool {
	_, exist := Conf.section[section]
	return exist
}

//CreateConfiguration 创建Configuration类型数据并返回
func CreateConfiguration(filename string) Configuration {
	return Configuration{
		name:       filename,
		section:    make(map[string]*Section),
		sectionNum: 0,
	}
}

//OpneFile 打开并读取ini文件
func OpneFile(filename string) (Configuration, error) {
	//初始化变量
	Conf := CreateConfiguration(filename)

	//自定义错误
	var errorNew errorNews

	//打开文件
	file, err := os.Open(filename)
	if err != nil {
		errorNew = "The file does not exist!"
		return Conf, errorNew
	}
	defer file.Close()

	buffer := bufio.NewReader(file)

	//key当前所属字段，默认为空
	section := ""
	Conf.section[section] = &Section{key: make(map[string]string), keyNum: 0}
	Conf.sectionNum++
	for {
		flag := 0
		line, err := buffer.ReadString(LineBreak)

		if err == io.EOF {
			flag = 1
		} else if err != nil {
			errorNew = "There was an error reading the file！"
			return Conf, errorNew
		}

		if line == "" || line[0] == CommentSign {
			if flag == 1 {
				break
			} else {
				continue
			}
		} else if line[0] == '[' {
			section = line[1 : len(line)-2]

			//判断是否已经读取过同样的字段
			if !Conf.ExistSection(section) {
				Conf.section[section] = &Section{make(map[string]string), 0}
				Conf.sectionNum++
			}
		} else {
			for i := 0; i < len(line); i++ {
				if line[i] == '=' {
					key := strings.Replace(line[0:i], " ", "", -1)
					value := strings.Replace(line[i+1:], " ", "", -1)
					value = strings.Replace(value, "\n", "", -1)
					Conf.GetKey(section).key[key] = value
					Conf.GetKey(section).keyNum++
					break
				}
			}
		}

		if flag == 1 {
			break
		}
	}
	return Conf, nil
}

//PrintConf 打印配置信息
func PrintConf(conf Configuration) {
	for section, key1 := range conf.section {
		fmt.Printf("[%s]\n", section)
		for key2, value := range key1.key {
			fmt.Printf("%s = %s\n", key2, value)
		}
		fmt.Printf("\n")
	}
}

//ListenFunc 监听文件并调用相应方法
func ListenFunc(filename string, listener Listener, Conf Configuration) {
	for {
		NewConf, err := OpneFile(filename)
		if err != nil {
			fmt.Println(err)
			return
		}

		//判断配置文件中属性是否发生变化
		if Conf.sectionNum != NewConf.sectionNum {
			listener.Listen(Conf, NewConf)
			Conf = NewConf
			return
		}
		for keySection, section := range Conf.section {
			if !NewConf.ExistSection(keySection) {
				listener.Listen(Conf, NewConf)
				Conf = NewConf
				return
			}
			NewSection := NewConf.GetKey(keySection)
			if section.keyNum != NewSection.keyNum {
				listener.Listen(Conf, NewConf)
				Conf = NewConf
				return
			}

			for key, value := range section.key {
				if !NewSection.ExistKey(key) || value != NewSection.GetValue(key) {
					listener.Listen(Conf, NewConf)
					Conf = NewConf
					return
				}
			}
		}

		time.Sleep(time.Duration(1) * time.Second)
	}
}

//Watch 读取文件并监听
func Watch(filename string, listener Listener) (Configuration, error) {

	Conf, err := OpneFile(filename)
	if err != nil {
		fmt.Println(err)
		return Conf, err
	}

	PrintConf(Conf)

	//开始监听文件,当文件发生改变时调用Listen方法
	fmt.Println("Listening...")
	ListenFunc(filename, listener, Conf)

	return Conf, nil
}
